/* Copyright © 2010 LPRM
Universidade Federal do Espírito Santo - RNP-GT CWTools

This file is part of GW-Android.

GW-Android is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

GW-Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GW-Android. If not, see <http://www.gnu.org/licenses/>. */
package br.ufes.cwtools.gw.android.components.tracker;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.app.ListActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
import br.ufes.cwtools.gw.android.components.Component;
import br.ufes.cwtools.gw.android.components.ComponentLifecycleHelper;
import br.ufes.cwtools.gw.android.connection_service.ConnectionServiceConnection;
import br.ufes.cwtools.gw.android.connection_service.IConnectionService;
import br.ufes.cwtools.gw.android.connection_service.ConnectionServiceConnection.NotConnectedException;
import br.ufes.cwtools.gw.android.location_service.GeoObject;
import br.ufes.cwtools.gw.android.location_service.GeoObjectsParser;
import br.ufes.cwtools.gw.android.location_service.ILocationService;

public class Tracker extends ListActivity{
	private static final long PERIOD = 180000; //3 minutos
	private static final String LOG_TAG = "Tracker";
	
	private static final String TRACKER_SUFIX = "/list/${lat}/${lng}/${range}?_format=xml";
	private static final int DEFAULT_RANGE = 1000;
	private String trackerUrl, trackerCustomUrl;
	private double lat, lon;
	
	private TrackerAdapter adapter;
	private Timer friendUpdateTimer;
	
	private List<GeoObject> geoObjects;
	private UserHandler mHandler;
	
	public static final int MESSAGE_USER_UPDATE = 1;
	public static final int MESSAGE_USER_CLEAR = 2;
	public static final int MESSAGE_USER_ADD = 3;
	
	private InputStream is = null;
	private String lastDownloadedUserList;
	
	private String sourceId;
	
	private boolean isConnected = false;
	private boolean isLocationed = false;
	
	private ILocationService locationService;
	
	@Override
	protected void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		setContentView(R.layout.tracker_view);
		
		trackerUrl = getIntent().getStringExtra(Component.EXTRA_APPLICATION_URL) + getIntent().getStringExtra(Component.EXTRA_BASE_URL);
		if(trackerUrl == null){
			Log.e(LOG_TAG, "Erro! Variável EXTRA_BASE_URL não informada nos Extras.");
			finish();
		}
		trackerUrl = trackerUrl + TRACKER_SUFIX;
		Log.d(LOG_TAG, trackerUrl);
		geoObjects = new ArrayList<GeoObject>();
		
		mHandler = new UserHandler();
		
		adapter = new TrackerAdapter(Tracker.this, R.layout.tracker_list_item, geoObjects);
		setListAdapter(adapter);
		
		
		bindService(new Intent(ILocationService.class.getName()), mLocation, Context.BIND_AUTO_CREATE);
		bindService(new Intent(IConnectionService.class.getName()), mConnection, Context.BIND_AUTO_CREATE);
		
		
		friendUpdateTimer = new Timer();
	    friendUpdateTimer.scheduleAtFixedRate(new GetInfoFromServer(), 0, PERIOD);
		
	    getListView().setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
				getIntent().putExtra("br.ufes.android.components.extra_geo_objects_url", lastDownloadedUserList);
				ComponentLifecycleHelper.selectNextComponent(Tracker.this, "relation", true, true);
			}
		});
	}
	
	@Override
	public void onDestroy(){
		super.onDestroy();
		try{
			unbindService(mConnection);
			unbindService(mLocation);
		}catch(Exception e){
			Log.e(LOG_TAG, e.getMessage(), e);
		}
	}
	
	@Override
	protected void onResume() {
		if(friendUpdateTimer == null){
			friendUpdateTimer = new Timer();
		    friendUpdateTimer.scheduleAtFixedRate(new GetInfoFromServer(), 0, PERIOD);
		}
		super.onResume();
	}
	
	@Override
	protected void onPause() {
		friendUpdateTimer.cancel();
		friendUpdateTimer = null;
		super.onPause();
	}
	
	private void downloadFirstXml(){
		if (isConnected && isLocationed){
			try {
				sourceId = ConnectionServiceConnection.generateId();
				mConnection.registerGetRequest(trackerCustomUrl, sourceId, false);
			} catch (NotConnectedException e) {
				e.printStackTrace();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void downloadXmlPeriodically(){
		if (isConnected && isLocationed){
			try {
				lat = locationService.getLatitude();
				lon = locationService.getLongitude();
				trackerCustomUrl = trackerUrl
					.replace("${lat}", String.valueOf(lat))
					.replace("${lng}", String.valueOf(lon))
					.replace("${range}", String.valueOf(DEFAULT_RANGE));
				sourceId = ConnectionServiceConnection.generateId();
				mConnection.registerGetRequest(trackerCustomUrl, sourceId, false);
			} catch (NotConnectedException e) {
				e.printStackTrace();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}
	
	private class GetInfoFromServer extends TimerTask{
		public void run(){
			Log.d(LOG_TAG, "Buscando usuários próximos no servidor.");

			downloadXmlPeriodically();
			
			//FIXME Tá construindo com o xml possivelmente defasado. 
			/*List<GeoObject> gol = GeoObjectsParser.parse(is);

			if (gol == null) {
				Log.e(LOG_TAG, "Parser returned null GeoObject list.");
				return;
			}
			
			mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_CLEAR, null));
			mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_ADD, gol));
			mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_UPDATE, null));
			
			Log.d(LOG_TAG, "Objetos lidos: " + gol.size());*/
		}
	}
	
	private final ServiceConnection mLocation = new ServiceConnection() {
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			locationService = ILocationService.Stub.asInterface(service);
			try {
				isLocationed = true;
				lat = locationService.getLatitude();
				lon = locationService.getLongitude();
				trackerCustomUrl = trackerUrl
										.replace("${lat}", String.valueOf(lat))
										.replace("${lng}", String.valueOf(lon))
										.replace("${range}", String.valueOf(DEFAULT_RANGE));
				Log.d(LOG_TAG, "Tracker Custom Url constructed: " + trackerCustomUrl);

				downloadFirstXml();
				
			} catch (RemoteException e) {
				e.printStackTrace();
			}catch (NullPointerException e){
				Log.e(LOG_TAG, "Localização ainda não foi fixada! Utilizando Localização da UFES");
				//Mudando a Url para utilizar a localização da UFES
				trackerCustomUrl = trackerUrl
					.replace("${lat}", "-20.27324080467165")
					.replace("${lng}", "-40.30574798583867")
					.replace("${range}", "5");
				Log.d(LOG_TAG, "Tracker Custom Url constructed: " + trackerCustomUrl);

				Toast.makeText(Tracker.this, "Localização ainda não foi fixada! Utilizando Localização do UFES", Toast.LENGTH_LONG).show();
			}
			
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {	}
		
	};
	

	public void atualizarOnclick(View view){
		friendUpdateTimer.schedule(new GetInfoFromServer(), 0);
	}	
	
	
	private final ConnectionServiceConnection mConnection = new ConnectionServiceConnection(Tracker.class.getName()) {

		@Override
		public void onServiceConnected() {
			isConnected = true;
			downloadFirstXml();		
		}
		
		@Override
		public void onRequestFinished(String id, String filePath) {
			Log.d(LOG_TAG, "Callback de download recebido.");
			mMapHandler.sendMessage(mMapHandler.obtainMessage(TRACKER_MSG, filePath));
			unbindService(mConnection);
		}
		
	};
	
	private static final int TRACKER_MSG = 1;
	
	private Handler mMapHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case TRACKER_MSG:
				String filePath = (String) msg.obj;
				try {
					is = new FileInputStream(filePath);
					
					//Testando aqui essa parte
					List<GeoObject> gol = GeoObjectsParser.parse(is);

					if (gol == null) {
						Log.e(LOG_TAG, "Parser returned null GeoObject list.");
						return;
					}
					
					mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_CLEAR, null));
					mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_ADD, gol));
					mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_UPDATE, null));
					
					Log.d(LOG_TAG, "Objetos lidos: " + gol.size());
					//------------------------
					
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (NullPointerException e) {
					Log.e(LOG_TAG,"Download returned null file");
				}
				break;
			default:
				super.handleMessage(msg);
			}
		}
	};
	
	public String inputStreamToString(InputStream is){
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		byte [] buffer = new byte[1024];
		while(true) {
			int len = 0;
			try {
				len = is.read(buffer);
			} catch (IOException e) {
				e.printStackTrace();
			}
			if(len < 0) {
				break;
			}
			bout.write(buffer, 0, len);
		}
		final byte [] data = bout.toByteArray();
		return new String (data);
	}
	
	private class TrackerAdapter extends ArrayAdapter<GeoObject>{
		private List<GeoObject> geo;
		
		
		public TrackerAdapter(Context context, int textViewResourceId, List<GeoObject> items) {
			super(context, textViewResourceId, items);
			geo = items;
		}
		
		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View v = convertView;
			if (v == null){
				LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = vi.inflate(R.layout.tracker_list_item, null);
			}
			GeoObject campaign = geo.get(position);
			if (campaign != null) {
				((TextView) v.findViewById(R.id.friend_name)).setText(campaign.getLabel());
				((TextView) v.findViewById(R.id.friend_lat_lon)).setText("Latitude: " + ((double)campaign.getGeoPoint().getLatitudeE6())/1e6 + "\nLongitude: " + ((double)campaign.getGeoPoint().getLongitudeE6())/1e6);
			}
			return v;
		}
	}
	
	private class UserHandler extends Handler{
		@SuppressWarnings("unchecked")
		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			switch(msg.what){
				case MESSAGE_USER_UPDATE:
					adapter.notifyDataSetChanged();
					break;
				case MESSAGE_USER_CLEAR:
					adapter.clear();
					break;
				case MESSAGE_USER_ADD:
					List<GeoObject> gol = (List<GeoObject>) msg.obj;
					for(GeoObject g:gol) adapter.add(g);
					break;
				default:
					super.handleMessage(msg);
			}
		}
	}
}
